package com.codi.bus.core.service.impl;

import com.codi.base.log.annotation.Loggable;
import com.codi.base.util.*;
import com.codi.bus.constant.GlobalConstant;
import com.codi.bus.constant.PayTypeConst;
import com.codi.bus.constant.RuleConstant;
import com.codi.bus.core.dao.*;
import com.codi.bus.core.domain.*;
import com.codi.bus.core.event.EventBusService;
import com.codi.bus.core.event.EventTypeDef;
import com.codi.bus.core.event.model.TradeEvent;
import com.codi.bus.core.resultModel.AllotTradeResult;
import com.codi.bus.core.resultModel.BaseResult;
import com.codi.bus.core.resultModel.FixAllotResult;
import com.codi.bus.core.service.*;
import com.codi.bus.core.service.model.ApplyModel;
import com.codi.bus.core.service.model.ShareTypeModel;
import com.codi.bus.core.service.model.TradeBuyModel;
import com.codi.bus.core.service.model.TradeSellModel;
import com.codi.bus.core.service.util.ObjectCreator;
import com.codi.bus.core.vo.BasicSellVo;
import com.codi.bus.core.vo.BuyVo;
import com.codi.bus.core.vo.SellVo;
import com.codi.bus.core.vo.TradeStatusVo;
import com.codi.bus.exception.BaseException;
import com.codi.bus.exception.SystemDBException;
import com.codi.fcloud.ufx.HsResponseUtil;
import com.codi.fcloud.ufx.constant.HSOutputParameterNames;
import com.codi.fcloud.ufx.constant.HsConst;
import com.codi.message.constant.ErrorConst;
import com.codi.message.constant.ErrorConstant;
import com.codi.message.util.MessageUtil;
import com.codi.trade.dto.HsAllotTradeDto;
import com.codi.trade.dto.HsRedeemTradeDto;
import com.codi.trade.service.*;
import com.google.common.base.Strings;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.codi.base.util.DateUtils.dateToTime;


@Service("tradeService")
@Transactional
public class TradeServiceImpl implements TradeService {

    private static final Logger logger = LoggerFactory.getLogger(TradeService.class);

    @Autowired
    private LocaldataScheduledService localdataScheduledService;
    @Resource(name = "transactionDao")
    private TransactionDao transactionDao;

    @Resource(name = "tradeOrderDao")
    private TradeOrderDao tradeOrderDao;

    @Resource(name = "orderFundDao")
    private OrderFundDao orderFundDao;

    @Resource(name = "userDao")
    private UserDao userDao;

    @Resource(name = "userSessionDao")
    private UserSessionDao userSessionDao;

    @Resource(name = "userBankDao")
    private UserBankDao userBankDao;

    @Resource(name = "allotTradeService")
    private AllotTradeService allotTradeService;

    @Resource(name = "redeemTradeService")
    private RedeemTradeService redeemTradeService;

    @Resource(name = "undoTradeApplyService")
    private UndoTradeApplyService undoTradeApplyService;

    @Resource(name = "queryService")
    private QueryService queryService;

    @Resource(name = "fixAllotTradeService")
    private FixAllotTradeService fixAllotTradeService;

    @Resource(name = "lookupDao")
    private LookupDao lookupDao;

    @Resource(name = "fixModifyTradeService")
    private FixModifyTradeService fixModifyTradeService;

    @Resource(name = "fastRedeemService")
    private FastRedeemService fastRedeemService;

    @Autowired
    private BankService bankService;

    @Autowired
    private TradeOrderService tradeOrderService;

    @Autowired
    private LookupService lookupService;

    @Autowired
    private BankLimitService bankLimitService;

    @Autowired
    private EventBusService eventBusService;

    @Autowired
    private MessageService messageService;
    @Autowired
    private LocaldataTradeOrderService localdataTradeOrderService;

    @Autowired
    private TradeOrdersDao tradeOrdersDao;

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult undoReal(Long userId, String channelId, String originalApplyNo, String tradePassword, boolean isOrg) throws BaseException {
        BaseResult result = new BaseResult();
        User user = userDao.getEntity(userId);
        try {
            // 判断此交易是否可以撤单
            ApplyModel applyModel = queryService.queryApply(originalApplyNo);

            if (applyModel == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_ORDER_FIND);
                return result;
            }
            applyModel.setIsOrg(isOrg);
            /*if (!applyModel.getUndoStatus()) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_BUSINESS);
                result.setErrorCode(ErrorConstant.ERROR_UNDO_DISABLE);
                return result;
            }*/
            Map<String, Object> map = undoTradeApplyService.undo(originalApplyNo, MD5.MD5Encode(tradePassword),
                applyModel.getTradeAcco(), channelId);
            String externalUndoNo = "";
            if (!HsResponseUtil.isOperationOk(map)) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                logger.error("undo - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);
                // 返回的申请编号
                externalUndoNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();

                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalUndoNo, 0, channelId, applyModel.getTradeAcco(), user.getExternalClientId());
                TradeOrders tradeOrders = TradeOrders.builder().taconfirmFlag("4").allotNo(originalApplyNo).build();
                tradeOrdersDao.updateByApplyNo(tradeOrders);

                OrderFund orderFund = orderFundDao.getEntityByExtApplyNo(originalApplyNo);
                if (orderFund == null) {
                    logger.warn("不存在的订单号={}", originalApplyNo);
                } else {
                    orderFund.setExternalUndoNo(externalUndoNo);
                    orderFund.setFundStatus(GlobalConstant.FUND_STATUS_CANCEL);
                    // 更新订单基金表
                    orderFundDao.updateStatus(orderFund);

                    TradeOrder tradeOrder = tradeOrderDao.getEntityByOrderId(orderFund.getOrderId());
                    if (tradeOrder == null) {
                        logger.warn("不存在tradeOrder");
                    } else {
                        // 撤单
                        tradeOrder.setOrderStatus(GlobalConstant.ORDER_STATUS_CANCEL);
                        // 更新状态
                        tradeOrderDao.updateStatus(tradeOrder);
                    }
                }
                TradeEvent tradeEvent = TradeEvent.builder()
                    .allotNo(externalUndoNo)
                    .build();
                tradeEvent.setEventType(EventTypeDef.TRADE_UNDO);
                eventBusService.postAsync(tradeEvent);
                //插入消息
                messageService.updateUndoUserMsg(userId, originalApplyNo, null);
            }

            // 插入交易记录流水
            Transaction transaction = ObjectCreator.createTransaction(map);
            // 不管成功或失败，都插入交易流水
            transactionDao.add(transaction);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "undo - Exception:", exception);
            throw new SystemDBException("undo Failure!", exception);
        }

    }

    @Override
    public BaseResult undo(Long userId, String token, String originalApplyNo, String tradePassword)
        throws BaseException {
        logger.info("undo - userId={},originalApplyNo={}", userId, originalApplyNo);
        BaseResult result = new BaseResult();
        // 获取channel Id
        UserSession userSession = userSessionDao.getEntityByToken(token);
        // 判断Session是否存在
        if (userSession == null) {
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_LOGIN_IN_OTHER_PLACE);
            return result;
        }
        User user = userDao.getEntity(userId);
        if (user == null) {
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_USER_FIND);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_USER_FIND));
            return result;
        }
        return undoReal(userId, userSession.getChannelId(), originalApplyNo, tradePassword, user.getIsOrg());
    }

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult buy(Long userId, String token, String bankAccount, String capitalMode, Integer moneyType,
                          String fundName, String fundCode, BigDecimal fundBalance, String shareType, String tradePassword,
                          String applyNo) throws BaseException {

        logger.info("buy - userId={}, bankAccount={}, capitalMode={}, moneyType={}, fundName={}, fundCode={}, fundBalance={},shareType={}",
            userId, bankAccount, capitalMode, moneyType, fundName, fundCode, fundBalance, shareType);
        AllotTradeResult result = new AllotTradeResult();
        try {

            UserSession userSession = userSessionDao.getEntityByToken(token);
            UserBank userBank = userBankDao.getEntityByBankNo(userId, bankAccount, capitalMode);

            // 判断Session是否存在
            if (userSession == null) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_LOGIN_IN_OTHER_PLACE);
                return result;
            }

            // 判断是否有银行卡
            if (userBank == null || StringUtil.isEmpty(userBank.getExternalTradeAccount())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                return result;
            }

            // 根据恒生银行编号
            String bankHSCode = "";
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            for (Lookup lookup : lookups) {
                if (lookup.getComponentValue().equals(userBank.getBankCode())) {
                    bankHSCode = lookup.getRuleValue();
                    break;
                }
            }
            if (StringUtil.isEmpty(bankHSCode)) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                    ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, userBank.getBankCode()));
                return result;
            }

            User user = userDao.getEntity(userId);

            // 调用接口服务
            Map<String, Object> map;

            if (user.getIsOrg()) {

                HsAllotTradeDto allotTradeDto = HsAllotTradeDto.builder()
                    .capital_mode(capitalMode)
                    .money_type(moneyType.toString())
                    .trade_acco(userBank.getExternalTradeAccount())
                    .password(MD5.MD5Encode(tradePassword))
                    .fund_code(fundCode)
                    .bank_no(bankHSCode)
                    .bank_account(userBank.getUserBankNo())
                    .balance(fundBalance.toString())
                    .share_type(shareType)
                    .trade_source(userSession.getChannelId())
                    .build();

                map = allotTradeService.buy(allotTradeDto);
            } else {
                map = allotTradeService.buy(capitalMode, moneyType, userBank.getExternalTradeAccount(),
                    MD5.MD5Encode(tradePassword), fundCode, bankHSCode, userBank.getUserBankNo(), fundBalance,
                    shareType, userSession.getChannelId());
            }

            Integer orderStatus = 0;
            Integer fundStatus = 0;
            String externalApplyNo = "";
            if (!HsResponseUtil.isOperationOk(map)) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
                logger.error("buy - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);

                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
                result.setAllotNo(externalApplyNo);
                // 申请日期
                result.setApplyDate(map.get(HSOutputParameterNames.APPLY_DATE).toString());
                // 申请时间
                result.setApplyTime(map.get(HSOutputParameterNames.APPLY_TIME).toString());
                // 发生金额
                result.setBalance(
                    BigDecimalUtil.ConvertToBigDecimal(map.get(HSOutputParameterNames.BALANCE).toString()));
                // 清算日期
                result.setClearDate(map.get(HSOutputParameterNames.CLEAR_DATE).toString());
                // 下单日期
                result.setOrderDate(DateUtils.dateToSimpleStr(new Date()));
                // 下单时间
                result.setOrderTime(dateToTime(new Date()));

                TradeEvent event = TradeEvent.builder()
                    .userId(userId)
                    .payType(PayTypeConst.BANK)
                    .fundCode(fundCode)
                    .balance(fundBalance)
                    .allotNo(externalApplyNo)
                    .count(0)
                    .build();
                event.setEventType(EventTypeDef.TRADE_BUY);

                eventBusService.postAsync(event);

            }

            // 订单流水号
            String orderSn = applyNo + userId.longValue();
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), new BigDecimal(0),
                fundBalance, orderSn, userId, userSession.getChannelId(), GlobalConstant.TRADE_TYPE_BUY,
                orderStatus, new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus, 0L, "", "");

            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "buy - Exception:", exception);
            throw new SystemDBException("buy Failure!", exception);
        }
    }

    @Override
    @Transactional
    public BaseResult buy(BuyVo vo) {
        BaseResult result = new BaseResult();
        result.setSuccess(false);

//
        UserBank userBank = bankService.get(vo.getUserId(), vo.getTradeAcco());
        if (userBank == null) {
            result.setErrorCode(ErrorConst.TRADE_ACCOUNT_NOT_FUND);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.TRADE_ACCOUNT_NOT_FUND));
            return result;
        }

        // 根据恒生银行编号
        String bankHSCode = "";
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        for (Lookup lookup : lookups) {
            if (lookup.getComponentValue().equals(userBank.getBankCode())) {
                bankHSCode = lookup.getRuleValue();
                break;
            }
        }
        if (StringUtil.isEmpty(bankHSCode)) {
            result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, userBank.getBankCode()));
            return result;
        }

        ShareTypeModel shareTypeModel = queryService.getShareType(vo.getFundCode());
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND));
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            return result;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        String capitalMode = userBank.getCapitalMode();
        String tradeType = GlobalConstant.TRADE_TYPE_BUY;

        Map<String, Object> map = null;

        String errorMsg;

        switch (vo.getPayType()) {

            case PayTypeConst.T0: //sell
                ShareTypeModel orginShareTypeModel = queryService.getShareType(vo.getOrginFundCode());
                if (orginShareTypeModel == null || StringUtil.isEmpty(orginShareTypeModel.getShareType())) {
                    result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                    result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
                    result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND));
                    return result;
                }
                tradeType = GlobalConstant.TRADE_TYPE_SELL_THEN_BUY;

                HsRedeemTradeDto redeemDto = HsRedeemTradeDto.builder()
                    .capital_mode(capitalMode)
                    .trade_acco(vo.getTradeAcco())
                    .password(MD5.MD5Encode(vo.getTradePassword()))
                    .fund_code(vo.getOrginFundCode())
                    .share_type(orginShareTypeModel.getShareType())
                    .shares(vo.getFundBalance().toString())
                    .trade_source(vo.getChannelId())
                    // 目标
                    .target_fund_code(vo.getFundCode())
                    .target_share_type(shareType)
                    .busin_ass_code(HsConst.BUSIN_ASS_CODE_CURRENCY_SEEL_THEN_BUY)
                    .build();


                map = redeemTradeService.sell(redeemDto);
                break;


            case PayTypeConst.OFFLINE:
                capitalMode = GlobalConstant.CAPTIAL_MODE_K;

                // 最小金额

                Lookup lookup = lookupService.getLookup(RuleConstant.TYPE_OFFLINE_BUY,
                    RuleConstant.VALUE_OFFLINE_BUY_MIN_VALUE);

                if (lookup != null) {
                    String minValueStr = lookup.getComponentValue();
                    if (!Strings.isNullOrEmpty(minValueStr)) {
                        if (!EqualsUtil.equals("-1", minValueStr)) {
                            if (vo.getFundBalance().compareTo(new BigDecimal(minValueStr)) == -1) {
                                result.setErrorCode(ErrorConst.OFFLINE_MIN_VALUE_LIMIT);
                                errorMsg = MessageUtil.getErrorMsg(ErrorConst.OFFLINE_MIN_VALUE_LIMIT
                                    , minValueStr);
                                result.setErrorMessage(errorMsg);
                                return result;
                            }
                        }
                    }
                }

                tradeType = GlobalConstant.TRADE_TYPE_OFFLINE_BUY;
                // 调用接口服务

                HsAllotTradeDto allotTradeDto = HsAllotTradeDto.builder()
                    .capital_mode(capitalMode)
                    .money_type(vo.getMoneyType().toString())
                    .trade_acco(vo.getTradeAcco())
                    .password(MD5.MD5Encode(vo.getTradePassword()))
                    .fund_code(vo.getFundCode())
                    .bank_no(bankHSCode)
                    .bank_account(userBank.getUserBankNo())
                    .balance(vo.getFundBalance().toString())
                    .share_type(shareType)
                    .trade_source(vo.getChannelId())
                    .build();

                map = allotTradeService.buy(allotTradeDto);

//                map = allotTradeService.buy(capitalMode, vo.getMoneyType(), vo.getTradeAcco(),
//                    MD5.MD5Encode(vo.getTradePassword()), vo.getFundCode(), bankHSCode, userBank.getUserBankNo(),
//                    vo.getFundBalance(), shareType, vo.getChannelId());
                break;

            case PayTypeConst.BANK: //normal

                //银行卡限额校验
                BankLimit bankLimit = bankLimitService.selectByCodeAndChannel(userBank.getBankCode(), userBank.getCapitalMode());

                BigDecimal limit;

                if (bankLimit != null) {
                    // 单笔
                    if (bankLimit.getSingleLimit() == null || bankLimit.getSingleLimit() == -1) {
                    } else {
                        limit = BigDecimal.valueOf(bankLimit.getSingleLimit());
                        if (vo.getFundBalance().compareTo(limit) == 1) {
                            result.setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_SINGLE_DEAL);
                            errorMsg = MessageUtil.getErrorMsg(ErrorConst.WALLET_HAS_REACH_MAX_SINGLE_DEAL, limit);
                            result.setErrorMessage(errorMsg);
                            return result;
                        }
                    }

                    // 单日
                    if (bankLimit.getDayLimit() == null || bankLimit.getDayLimit() == -1) {
                    } else {
                        limit = BigDecimal.valueOf(bankLimit.getDayLimit());

                        BigDecimal daySum = tradeOrderService.getCurrentDayDealSum(vo.getTradeAcco());

                        BigDecimal total = MoneyUtils.add(daySum, vo.getFundBalance());

                        if (total.compareTo(limit) == 1) {
                            result.setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_DAY_DEAL);
                            errorMsg = MessageUtil.getErrorMsg(ErrorConst.OFFLINE_MIN_VALUE_LIMIT, limit);
                            result.setErrorMessage(errorMsg);
                            return result;
                        }
                    }

                    // 单月
                    if (bankLimit.getMonthLimit() == null || bankLimit.getMonthLimit() == -1) {
                    } else {
                        limit = BigDecimal.valueOf(bankLimit.getMonthLimit());

                        BigDecimal monthSum = tradeOrderService.getCurrentMonthDealSum(vo.getTradeAcco());

                        BigDecimal total = MoneyUtils.add(monthSum, vo.getFundBalance());

                        if (total.compareTo(limit) == 1) {
                            result.setErrorCode(ErrorConst.WALLET_HAS_REACH_MAX_MONTH_DEAL);
                            errorMsg = MessageUtil.getErrorMsg(ErrorConst.WALLET_HAS_REACH_MAX_MONTH_DEAL, limit);
                            result.setErrorMessage(errorMsg);
                            return result;
                        }
                    }
                }// end bankLimit != null

                tradeType = GlobalConstant.TRADE_TYPE_BUY;
                // 调用接口服务
                map = allotTradeService.buy(capitalMode, vo.getMoneyType(), vo.getTradeAcco(),
                    MD5.MD5Encode(vo.getTradePassword()), vo.getFundCode(), bankHSCode, userBank.getUserBankNo(),
                    vo.getFundBalance(), shareType, vo.getChannelId());
                break;
            default:
                break;
        }

        TradeBuyModel model = new TradeBuyModel();
        User user = userDao.getEntity(vo.getUserId());
        Integer orderStatus = 0;
        Integer fundStatus = 0;
        String externalApplyNo = "";
        if (!HsResponseUtil.isOperationOk(map)) {
            String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
            result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
            result.setErrorMessage(errorInfo);
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
            logger.error("buy - error info:{}", errorInfo);
        } else {
            result.setSuccess(true);

            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
            // 返回的申请编号
            externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
            model.setAllotNo(externalApplyNo);
            localdataTradeOrderService.addApplyOrder(vo.getUserId().intValue(), externalApplyNo, 0, vo.getChannelId(), vo.getTradeAcco(), user.getExternalClientId());
            if (PayTypeConst.T0.equals(vo.getPayType())) {
                localdataTradeOrderService.addApplyOrder(vo.getUserId().intValue(), externalApplyNo, 2, vo.getChannelId(), vo.getTradeAcco(), user.getExternalClientId());
            }
            Date now = new Date();
            String currentTime = DateUtils.dateToTime(now);
            // 申请日期
            model.setApplyDate(map.get(HSOutputParameterNames.APPLY_DATE).toString());
            // 申请时间
            model.setApplyTime(currentTime);
            // 发生金额

            if (EqualsUtil.equals(PayTypeConst.T0, vo.getPayType())) {
                model.setBalance(vo.getFundBalance());
            } else {
                model.setBalance(BigDecimalUtil.ConvertToBigDecimal(map.get(HSOutputParameterNames.BALANCE).toString()));
            }

            // 清算日期
            model.setClearDate(map.get(HSOutputParameterNames.CLEAR_DATE).toString());
            // 下单日期
            model.setOrderDate(DateUtils.dateToSimpleStr(now));
            // 下单时间
            model.setOrderTime(currentTime);

            //
//            TradeEvent event = TradeEvent.builder()
//                                         .userId(vo.getUserId())
//                                         .payType(vo.getPayType())
//                                         .fundCode(vo.getFundCode())
//                                         .balance(vo.getFundBalance())
//                                         .allotNo(externalApplyNo)
//                                         .count(0)
//                                         .build();
//            event.setEventType(EventTypeDef.TRADE_BUY);
//
//            eventBusService.postAsync(event);

        }

        // 订单流水号
        String orderSn = vo.getApplyNo() + vo.getUserId().longValue();
        // 插入订单数据到本地数据库
        storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), new BigDecimal(0),
            vo.getFundBalance(), orderSn, vo.getUserId(), vo.getChannelId(), tradeType,
            orderStatus, new Date(), externalApplyNo, vo.getFundCode(), vo.getFundName(), shareType,
            fundStatus, 0L, "", "");


        result.setResult(model);
        return result;
    }

    @Override
    @Transactional
    public BaseResult sellForWallet(SellVo sellVo) {
        BaseResult result = new BaseResult();
        result.setSuccess(false);
        User user = userDao.getEntity(sellVo.getUserId());
//
        UserBank userBank = bankService.get(sellVo.getUserId(), sellVo.getTradeAcco());
        if (userBank == null) {
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConst.TRADE_ACCOUNT_NOT_FUND));
            result.setErrorCode(ErrorConst.TRADE_ACCOUNT_NOT_FUND);
            return result;
        }

        // 根据恒生银行编号
        String bankHSCode = "";
        List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
        for (Lookup lookup : lookups) {
            if (lookup.getComponentValue().equals(userBank.getBankCode())) {
                bankHSCode = lookup.getRuleValue();
                break;
            }
        }
        if (StringUtil.isEmpty(bankHSCode)) {
            result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, userBank.getBankCode()));
            return result;
        }

        ShareTypeModel shareTypeModel = queryService.getShareType(sellVo.getFundCode());
        if (shareTypeModel == null || StringUtil.isEmpty(shareTypeModel.getShareType())) {
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND));
            return result;
        }

        // 获取份额类别
        String shareType = shareTypeModel.getShareType();

        String capitalMode = userBank.getCapitalMode();
        String tradeType = GlobalConstant.TRADE_TYPE_SELL_THEN_BUY_WALLET;

        Map<String, Object> map = null;


        ShareTypeModel newShareTypeModel = queryService.getShareType(sellVo.getNewFundCode());
        if (newShareTypeModel == null || StringUtil.isEmpty(newShareTypeModel.getShareType())) {
            result.setErrorCode(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorMessage(MessageUtil.getErrorMsg(ErrorConstant.ERROR_NO_SHARE_TYPE_FIND));
            return result;
        }

        HsRedeemTradeDto redeemDto = HsRedeemTradeDto.builder()
            .capital_mode(capitalMode)
            .trade_acco(sellVo.getTradeAcco())
            .password(MD5.MD5Encode(sellVo.getTradePassword()))
            .fund_code(sellVo.getFundCode())
            .share_type(shareType)
            .shares(sellVo.getFundShares().toString())
            .trade_source(sellVo.getChannelId())
            // 目标
            .target_fund_code(sellVo.getNewFundCode())
            .target_share_type(newShareTypeModel.getShareType())
            .busin_ass_code(HsConst.BUSIN_ASS_CODE_SELL_THEN_BUY_CURRENCY)
            .build();

        map = redeemTradeService.sell(redeemDto);

        TradeBuyModel model = new TradeBuyModel();

        Integer orderStatus = 0;
        Integer fundStatus = 0;
        String externalApplyNo = "";
        if (!HsResponseUtil.isOperationOk(map)) {
            String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
            result.setErrorMessage(errorInfo);
            result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
            logger.error("buy - error info:{}", errorInfo);
        } else {
            result.setSuccess(true);

            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
            // 返回的申请编号
            externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
            model.setAllotNo(externalApplyNo);

            Date now = new Date();
            String currentTime = DateUtils.dateToTime(now);
            // 申请日期
            model.setApplyDate(map.get(HSOutputParameterNames.APPLY_DATE).toString());
            // 申请时间
            model.setApplyTime(currentTime);
            // 发生金额

            model.setBalance(new BigDecimal(0));


            // 清算日期
            model.setClearDate(map.get(HSOutputParameterNames.CLEAR_DATE).toString());
            // 下单日期
            model.setOrderDate(DateUtils.dateToSimpleStr(now));
            // 下单时间
            model.setOrderTime(currentTime);

            //
            TradeEvent event = TradeEvent.builder()
                .userId(sellVo.getUserId())
                .fundCode(sellVo.getFundCode())
                .allotNo(externalApplyNo)
                .count(0)
                .build();
            event.setEventType(EventTypeDef.TRADE_SELL);

            eventBusService.postAsync(event);

            localdataTradeOrderService.addApplyOrder(sellVo.getUserId().intValue(), externalApplyNo, 2, sellVo.getChannelId(), sellVo.getTradeAcco(), user.getExternalClientId());


        }

        // 订单流水号
        String orderSn = sellVo.getApplyNo() + sellVo.getUserId().longValue();
        // 插入订单数据到本地数据库
        storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), sellVo.getFundShares(),
            new BigDecimal(0), orderSn, sellVo.getUserId(), sellVo.getChannelId(), tradeType,
            orderStatus, new Date(), externalApplyNo, sellVo.getFundCode(), sellVo.getFundName(), shareType,
            fundStatus, 0L, "", "");


        result.setResult(model);
        return result;
    }

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult sellReal(Long userId, String channelId, String bankAccount, String capitalMode, String fundName,
                               String fundCode, BigDecimal shares, String shareType, String tradePassword, String applyNo) throws BaseException {
        try {
            UserBank userBank = userBankDao.getEntityByBankNo(userId, bankAccount, capitalMode);
            User user = userDao.getEntity(userId);
            BaseResult result = new BaseResult();
            // 判断是否有银行卡
            if (userBank == null || StringUtil.isEmpty(userBank.getExternalTradeAccount())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                return result;
            }
            // 调用接口服务
            Map<String, Object> map = redeemTradeService.sell(capitalMode, userBank.getExternalTradeAccount(),
                MD5.MD5Encode(tradePassword), fundCode, shareType, shares, channelId);

            Integer fundStatus = 0;
            Integer orderStatus = 0;
            String externalApplyNo = "";
            if (!HsResponseUtil.isOperationOk(map)) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
                logger.error("sell - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);

                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalApplyNo, 0, channelId, userBank.getExternalTradeAccount(), user.getExternalClientId());


                TradeEvent event = TradeEvent.builder()
                    .userId(userId)
                    .payType(PayTypeConst.BANK)
                    .fundCode(fundCode)
                    .balance(shares)
                    .allotNo(externalApplyNo)
                    .build();
                event.setEventType(EventTypeDef.TRADE_SELL);

                eventBusService.postAsync(event);


            }

            // 插入数据到订单表
            // 订单流水号
            String orderSn = applyNo + userId.longValue();
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), shares, new BigDecimal(0),
                orderSn, userId, channelId, GlobalConstant.TRADE_TYPE_SELL, orderStatus,
                new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus, 0L, "", "");

            TradeSellModel model = new TradeSellModel();
            model.setSuccess(result.getSuccess());
            model.setFundCode(fundCode);
            model.setFundName(fundName);
            model.setAllotNo(externalApplyNo);
            Date now = new Date();
            String currentTime = DateUtils.dateToTime(now);
            // 下单日期
            model.setOrderDate(DateUtils.dateToSimpleStr(now));
            // 下单时间
            model.setOrderTime(currentTime);
            result.setResult(model);


            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "sell - Exception:", exception);
            throw new SystemDBException("sell Failure!", exception);
        }
    }


    @Override
    public BaseResult sell(Long userId, String token, String bankAccount, String capitalMode, String fundName,
                           String fundCode, BigDecimal shares, String shareType, String tradePassword, String applyNo)
        throws BaseException {
        logger.info("sell - userId={}, bankAccount={}, capitalMode={}, fundName={}, fundCode={}, shares={},shareType={}",
            userId, bankAccount, capitalMode, fundName, fundCode, shares, shareType);

        UserSession userSession = userSessionDao.getEntityByToken(token);
        BaseResult result = new BaseResult();

        // 判断Session是否存在
        if (userSession == null) {
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_LOGIN_IN_OTHER_PLACE);
            return result;
        }
        return sellReal(userId, userSession.getChannelId(), bankAccount, capitalMode, fundName, fundCode, shares, shareType, tradePassword, applyNo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public BaseResult sell(BasicSellVo vo) {

        logger.info("basic sell vo ={}", vo);

        BaseResult result = new BaseResult(false);

        UserBank userBank = vo.getUserBank();
        String fundCode = vo.getFundCode();
        String shareType = vo.getShareType();
        BigDecimal shares = vo.getShares();
        String channelId = vo.getChannelId();
        Long userId = vo.getUserId();
        String fundName = vo.getFundName();


        Map<String, Object> map = redeemTradeService.sell(userBank.getCapitalMode(), userBank.getExternalTradeAccount(),
            MD5.MD5Encode(vo.getTradePassword()), fundCode, shareType, shares, channelId);

        Integer fundStatus = 0;
        Integer orderStatus = 0;
        String externalApplyNo = "";
        if (!HsResponseUtil.isOperationOk(map)) {
            String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
            result.setSuccess(false);
            result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
            result.setErrorMessage(errorInfo);
            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
            logger.error("sell - error info:{}", errorInfo);
        } else {
            result.setSuccess(true);

            // 订单状态
            orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
            // 基金状态
            fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
            // 返回的申请编号
            externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();


            TradeEvent event = TradeEvent.builder()
                .userId(userId)
                .fundCode(fundCode)
                .balance(shares)
                .allotNo(externalApplyNo)
                .build();
            event.setEventType(EventTypeDef.TRADE_SELL);

            eventBusService.postAsync(event);
        }

        // 插入数据到订单表
        // 订单流水号
        String orderSn = vo.getApplyNo() + userId.longValue();
        // 插入订单数据到本地数据库
        storeData(map, userBank.getCapitalMode(), userBank.getBankCode(), userBank.getUserBankNo(),
            shares, new BigDecimal(0),
            orderSn, userId, channelId, GlobalConstant.TRADE_TYPE_SELL, orderStatus,
            new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus, 0L, "", "");

        TradeSellModel model = new TradeSellModel();
        model.setSuccess(result.getSuccess());
        model.setFundCode(fundCode);
        model.setFundName(fundName);
        model.setAllotNo(externalApplyNo);
        result.setResult(model);

        return result;
    }

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult fixReal(String applyNo, String tradePassword, Long protocolFixDay, String shareType,
                              String bankAccount, String fundName, String protocolPeriodUnit, long userId, BigDecimal balance,
                              String fundCode, String capitalMode, String channelId, String fixModel, String oldExternalScheduledProtocolId,
                              String expiryDate) throws BaseException {
        try {
            FixAllotResult result = new FixAllotResult();
            User user = userDao.getEntity(userId);
            UserBank userBank = userBankDao.getEntityByBankNo(userId, bankAccount, capitalMode);
            // 判断是否有银行卡
            if (userBank == null || StringUtil.isEmpty(userBank.getExternalTradeAccount())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                return result;
            }
            Map<String, Object> map = null;
            String tradeType = "";

            // 调用接口服务
            // 如果是变更，则调用变更接口
            boolean isApplyFix = false;
            if (GlobalConstant.FIX_MODEL_MODIFY.equals(fixModel) || GlobalConstant.FIX_MODEL_STOP.equals(fixModel)) {

                map = fixModifyTradeService.modifyFix(capitalMode, MD5.MD5Encode(tradePassword),
                    userBank.getExternalTradeAccount(), channelId, fundCode, balance, shareType,
                    protocolPeriodUnit, protocolFixDay, oldExternalScheduledProtocolId, fixModel, expiryDate);
                //判断是否变更成功  成功  更新数据到定投协议表
                String successType = map.get(HSOutputParameterNames.SUCCESS_TYPE).toString();
                if (successType.equals("0")) {
                    localdataScheduledService.update(userId, oldExternalScheduledProtocolId, bankAccount, userBank.getExternalTradeAccount());
                }
                // 如果不是变更，则申购接口
            } else {
                isApplyFix = true;
                map = fixAllotTradeService.fix(capitalMode, MD5.MD5Encode(tradePassword),
                    userBank.getExternalTradeAccount(), channelId, fundCode, balance, shareType,
                    protocolPeriodUnit, protocolFixDay);
                //判断是否申购成功  成功  插入数据到定投协议表
                String successType = map.get(HSOutputParameterNames.SUCCESS_TYPE).toString();
                if (successType.equals("0")) {
                    //申购成功后返回的定投协议号
                    String scheduledProtocolId = map.get(HSOutputParameterNames.SCHEDULED_PROTOCOL_ID).toString();
                    if (StringUtil.isNotEmpty(scheduledProtocolId)) {
                        localdataScheduledService.add(bankAccount, userBank.getExternalTradeAccount(), userId, scheduledProtocolId);
                    }
                }
            }
            boolean success = HsResponseUtil.isOperationOk(map);

            Integer orderStatus = 0;
            Integer fundStatus = 0;
            String externalApplyNo = "";
            if (!success) {
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);

                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;

                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;

                logger.error("fix - error info:{}", errorInfo);
            } else {
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();

                localdataTradeOrderService.addApplyOrder((int) userId, externalApplyNo, 0, channelId, userBank.getExternalTradeAccount(), user.getExternalClientId());


                // 返回数据
                setSuccessResult(result, map, externalApplyNo, balance, fundName, protocolFixDay, protocolPeriodUnit);

                if (isApplyFix) {
                    TradeEvent event = TradeEvent.builder()
                        .userId(userId)
                        .fundCode(fundCode)
                        .allotNo(externalApplyNo)
                        .count(0)
                        .build();
                    event.setEventType(EventTypeDef.TRADE_FIX);
                    eventBusService.postAsync(event);
                }
            }

            // 插入数据到订单表
            if (GlobalConstant.FIX_MODEL_MODIFY.equals(fixModel)) {
                tradeType = GlobalConstant.TRADE_TYPE_FIX_MODIFY;
            } else if (GlobalConstant.FIX_MODEL_STOP.equals(fixModel)) {
                tradeType = GlobalConstant.TRADE_TYPE_FIX_STOP;
            } else {
                tradeType = GlobalConstant.TRADE_TYPE_FIX;
            }

            // 定投协议号
            String externalScheduledProtocolId = GlobalConstant.FIX_MODEL_NORMAL.equals(fixModel)
                ? map.get(HSOutputParameterNames.SCHEDULED_PROTOCOL_ID).toString() : oldExternalScheduledProtocolId;

            // 订单号
            String orderSn = applyNo + userId;
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), new BigDecimal(0), balance,
                orderSn, userId, channelId, tradeType, orderStatus, new Date(), externalApplyNo,
                fundCode, fundName, shareType, fundStatus, protocolFixDay, protocolPeriodUnit,
                externalScheduledProtocolId);

            // 定投协议号
            result.setScheduledProtocolId(externalScheduledProtocolId);
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "fix - Exception:", exception);
            throw new SystemDBException("fix Failure!", exception);
        }

    }

    /**
     * 定投申购/修改
     */
    @Override
    public BaseResult fix(String applyNo, String tradePassword, Long protocolFixDay, String shareType,
                          String bankAccount, String fundName, String protocolPeriodUnit, long userId, BigDecimal balance,
                          String fundCode, String capitalMode, String token, String fixModel, String oldExternalScheduledProtocolId,
                          String expiryDate) throws BaseException {
        logger.info("fix - userId={}, applyNo={}, protocolFixDay={}, shareType={}, bankAccount={}, fundName={}, protocolPeriodUnit={}, balance={}, fundCode={}, capitalMode={},token={10}",
            userId, bankAccount, capitalMode, fundName, fundCode, balance, shareType);
        UserSession userSession = userSessionDao.getEntityByToken(token);
        return fixReal(applyNo, tradePassword, protocolFixDay, shareType, bankAccount, fundName, protocolPeriodUnit, userId, balance, fundCode, capitalMode, userSession.getChannelId(), fixModel, oldExternalScheduledProtocolId, expiryDate);
    }

    @Override
    @Transactional(rollbackFor = {BaseException.class})
    public BaseResult fastRedeemReal(Long userId, String channelId, String capitalMode, String tradePassword, String fundCode,
                                     String shareType, BigDecimal shares, String bankAccount, String fundName, String applyNo) {
        BaseResult result = new BaseResult();
        try {
            UserBank userBank = userBankDao.getEntityByBankNo(userId, bankAccount, capitalMode);
            User user = userDao.getEntity(userId);
            // 判断是否有银行卡
            if (userBank == null || StringUtil.isEmpty(userBank.getExternalTradeAccount())) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                return result;
            }

            // 获取恒生银行编号 shi.pengyan 2017-9-27 14:42:25
            String bankHSCode = "";
            List<Lookup> lookups = lookupDao.getLookupByType(RuleConstant.RULE_LOOKUP_TYPE_HSBANK);
            for (Lookup lookup : lookups) {
                if (lookup.getComponentValue().equals(userBank.getBankCode())) {
                    bankHSCode = lookup.getRuleValue();
                    break;
                }
            }
            if (StringUtil.isEmpty(bankHSCode)) {
                result.setSuccess(false);
                result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
                result.setErrorCode(ErrorConstant.ERROR_NO_BANK_CARD_FIND);
                result.setErrorMessage(MessageUtil.getErrorMessage(ErrorConstant.ERROR_NO_BANK_CARD_FIND,
                    ErrorConstant.PLACEHOLDER_FIELD_NO_FIND_FIELD, userBank.getBankCode()));
                return result;
            }
            // 调用恒生快速赎回接口
            Map<String, Object> map = fastRedeemService.fastRedeem(capitalMode, userBank.getExternalTradeAccount(),
                MD5.MD5Encode(tradePassword), fundCode, shareType, shares, bankHSCode, bankAccount,
                channelId);


            Integer fundStatus = 0;// 基金状态
            Integer orderStatus = 0;// 订单状态
            String externalApplyNo = "";
            if (!HsResponseUtil.isOperationOk(map)) { // fix result
                String errorInfo = map.get(HSOutputParameterNames.ERROR_INFO).toString();
                result.setSuccess(false);
                result.setErrorCode(ErrorConstant.ERROR_HUNDSUNAPI_WRONG);
                result.setErrorMessage(errorInfo);
                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY_FAIL;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_APPLY_FAIL;
                logger.error("fastRedeem - error info:{}", errorInfo);
            } else {
                result.setSuccess(true);

                // 订单状态
                orderStatus = GlobalConstant.ORDER_STATUS_APPLY;
                // 基金状态
                fundStatus = GlobalConstant.FUND_STATUS_AWAITING_CONFIRM;
                // 返回的申请编号
                externalApplyNo = map.get(HSOutputParameterNames.ALLOT_NO).toString();
                localdataTradeOrderService.addApplyOrder(userId.intValue(), externalApplyNo, 0, channelId, userBank.getExternalTradeAccount(), user.getExternalClientId());

                TradeEvent event = TradeEvent.builder()
                    .userId(userId)
                    .fundCode(fundCode)
                    .allotNo(externalApplyNo)
                    .build();
                event.setEventType(EventTypeDef.TRADE_FAST_REDEEM);
                eventBusService.postAsync(event);

                messageService.updatefastRedeemUserMsg(shares, fundName, userId, externalApplyNo, null);

            }

            // 插入数据到订单表
            // 订单流水号
            String orderSn = applyNo + userId.longValue();
            // 插入订单数据到本地数据库
            storeData(map, capitalMode, userBank.getBankCode(), userBank.getUserBankNo(), shares, new BigDecimal(0),
                orderSn, userId, channelId, GlobalConstant.TRADE_TYPE_FAST_REDEEM, orderStatus,
                new Date(), externalApplyNo, fundCode, fundName, shareType, fundStatus, 0L, "", "");
            return result;
        } catch (BaseException baseException) {
            throw baseException;
        } catch (Exception exception) {
            ExceptionUtil.logError(logger, "fast redeem - Exception:", exception);
            throw new SystemDBException("fast redeem - Failure!", exception);
        }

    }

    /**
     * 快速赎回
     *
     * @param userId        用户Id
     * @param token         Token
     * @param capitalMode   资金方式
     * @param tradePassword 交易密码
     * @param fundCode      基金代码
     * @param shareType     份额类型
     * @param shares        份额
     * @param bankAccount   银行账户
     * @param fundName      基金名称
     * @param applyNo       申请编号
     * @param tradeAcco     交易账号
     * @return 赎回结果
     */
    @Loggable
    @Override
    public BaseResult fastRedeem(Long userId, String token, String capitalMode, String tradePassword, String fundCode,
                                 String shareType, BigDecimal shares, String bankAccount, String fundName, String applyNo, String tradeAcco) {
        BaseResult result = new BaseResult();
        UserSession userSession = userSessionDao.getEntityByToken(token);
        // 判断Session是否存在
        if (userSession == null) {
            result.setSuccess(false);
            result.setErrorType(GlobalConstant.ERROR_TYPE_APPLICATION);
            result.setErrorCode(ErrorConstant.ERROR_LOGIN_IN_OTHER_PLACE);
            return result;
        }
        return fastRedeemReal(userId, userSession.getChannelId(), capitalMode, tradePassword, fundCode, shareType, shares, bankAccount, fundName, applyNo);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateTradeStatusSuc(TradeStatusVo tradeStatusVo) {
        TradeOrder record = new TradeOrder();

        record.setOrderId(tradeStatusVo.getTradeOrderId());
        record.setOrderStatus(GlobalConstant.ORDER_STATUS_CONFIRM);
        if (tradeStatusVo.getConfirmDate() != null) {
            record.setConfirmDate(tradeStatusVo.getConfirmDate());
        }

        tradeOrderDao.updateStatus(record);


        OrderFund orderFundRecord = new OrderFund();

        orderFundRecord.setOrderFundId(tradeStatusVo.getOrderFundId());
        orderFundRecord.setFundStatus(GlobalConstant.FUND_STATUS_CONFIRM_SUC);

        orderFundDao.updateStatus(orderFundRecord);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateTradeStatusFail(TradeStatusVo tradeStatusVo) {
        TradeOrder record = new TradeOrder();

        record.setOrderId(tradeStatusVo.getTradeOrderId());
        record.setOrderStatus(GlobalConstant.ORDER_STATUS_CONFIRM_FAIL);
        if (tradeStatusVo.getConfirmDate() != null) {
            record.setConfirmDate(tradeStatusVo.getConfirmDate());
        }

        tradeOrderDao.updateStatus(record);


        OrderFund orderFundRecord = new OrderFund();

        orderFundRecord.setOrderFundId(tradeStatusVo.getOrderFundId());
        orderFundRecord.setFundStatus(GlobalConstant.FUND_STATUS_APPLY_FAIL);

        orderFundDao.updateStatus(orderFundRecord);
    }


    /***
     * 插入订单数据到本地数据库
     *
     * @param map
     * @param capitalMode
     * @param bankCode
     * @param bankAccount
     * @param shares
     * @param orderBalance
     * @param orderSn
     * @param userId
     * @param channelId
     * @param tradeType
     * @param orderStatus
     * @param applyDate
     * @param externalApplyNo
     * @param fundCode
     * @param fundName
     * @param shareType
     * @param fundStatus
     * @param protocolFixDay
     * @param protocolPeriodUnit
     * @param externalScheduledProtocolId
     */
    @Override
    public void storeData(Map<String, Object> map, String capitalMode, String bankCode, String bankAccount,
                          BigDecimal shares, BigDecimal orderBalance, String orderSn, Long userId, String channelId, String tradeType,
                          Integer orderStatus, Date applyDate, String externalApplyNo, String fundCode, String fundName,
                          String shareType, Integer fundStatus, Long protocolFixDay, String protocolPeriodUnit,
                          String externalScheduledProtocolId) {
        if (StringUtils.isBlank(externalApplyNo)) {
            return;
        }

        // 插入数据到订单表
        TradeOrder tradeOrder = ObjectCreator.createTradeOrder(capitalMode, bankCode, bankAccount, orderBalance,
            orderSn, userId, channelId, tradeType, orderStatus, applyDate);
        // 插入数据到订单表
        tradeOrderDao.add(tradeOrder);

        // 订单基金表
        OrderFund orderFund = ObjectCreator.createOrderFund(tradeOrder.getOrderId(), shares, orderBalance, fundCode,
            fundName, externalApplyNo, shareType, fundStatus, protocolFixDay, protocolPeriodUnit,
            externalScheduledProtocolId);
        // 插入数据到订单基金表
        orderFundDao.add(orderFund);

        // 插入交易记录流水
        Transaction transaction = ObjectCreator.createTransaction(map);
        transaction.setOrderId(tradeOrder.getOrderId());
        // 不管成功或失败，都插入交易流水
        transactionDao.add(transaction);
    }

    private void setSuccessResult(FixAllotResult result, Map<String, Object> map, String externalApplyNo,
                                  BigDecimal balance, String fundName, Long protocolFixDay, String protocolPeriodUnit) {
        result.setSuccess(true);
        result.setAllotNo(externalApplyNo);
        // 下次扣款日期
        String nextFixDate = map.get(HSOutputParameterNames.NEXT_FIX_REQUEST_DATE).toString();
        // 将日期转换成YYYY-MM-DD
        result.setNextFixRequestDate(DateUtils.String8ToDateStr(nextFixDate));
        // 金额
        result.setBalance(balance);
        // 基金名称
        result.setFundName(fundName);
        // 定投周期
        result.setProtocolFixDay(protocolFixDay);
        // 扣款日期
        result.setProtocolPeriodUnit(protocolPeriodUnit);
    }

}
